{
  stdenv,
  lib,
  makeWrapper,
  pkg-config,
  mono,
  dotnetbuildhelpers,
}:

attrsOrig@{
  pname,
  version,
  nativeBuildInputs ? [ ],
  xBuildFiles ? [ ],
  xBuildFlags ? [ "/p:Configuration=Release" ],
  outputFiles ? [ "bin/Release/*" ],
  dllFiles ? [ "*.dll" ],
  exeFiles ? [ "*.exe" ],
  # Additional arguments to pass to the makeWrapper function, which wraps
  # generated binaries.
  makeWrapperArgs ? [ ],
  ...
}:
let
  arrayToShell = (a: toString (map (lib.escape (lib.stringToCharacters "\\ ';$`()|<>\t")) a));

  attrs = {
    inherit pname version;

    nativeBuildInputs = [
      pkg-config
      makeWrapper
      dotnetbuildhelpers
      mono
    ]
    ++ nativeBuildInputs;

    configurePhase = ''
      runHook preConfigure

      [ -z "''${dontPlacateNuget-}" ] && placate-nuget.sh
      [ -z "''${dontPlacatePaket-}" ] && placate-paket.sh
      [ -z "''${dontPatchFSharpTargets-}" ] && patch-fsharp-targets.sh

      runHook postConfigure
    '';

    buildPhase = ''
      runHook preBuild

      echo Building dotNET packages...

      # Probably needs to be moved to fsharp
      if pkg-config FSharp.Core
      then
        export FSharpTargetsPath="$(dirname $(pkg-config FSharp.Core --variable=Libraries))/Microsoft.FSharp.Targets"
      fi

      ran=""
      for xBuildFile in ${arrayToShell xBuildFiles} ''${xBuildFilesExtra}
      do
        ran="yes"
        xbuild ${arrayToShell xBuildFlags} ''${xBuildFlagsArray} $xBuildFile
      done

      [ -z "$ran" ] && xbuild ${arrayToShell xBuildFlags} ''${xBuildFlagsArray}

      runHook postBuild
    '';

    dontStrip = true;

    installPhase = ''
      runHook preInstall

      target="$out/lib/dotnet/${pname}"
      mkdir -p "$target"

      cp -rv ${arrayToShell outputFiles} "''${outputFilesArray[@]}" "$target"

      if [ -z "''${dontRemoveDuplicatedDlls-}" ]
      then
        pushd "$out"
        remove-duplicated-dlls.sh
        popd
      fi

      set -f
      for dllPattern in ${arrayToShell dllFiles} ''${dllFilesArray[@]}
      do
        set +f
        for dll in "$target"/$dllPattern
        do
          [ -f "$dll" ] || continue
          if pkg-config $(basename -s .dll "$dll")
          then
            echo "$dll already exported by a buildInputs, not re-exporting"
          else
            create-pkg-config-for-dll.sh "$out/lib/pkgconfig" "$dll"
          fi
        done
      done

      set -f
      for exePattern in ${arrayToShell exeFiles} ''${exeFilesArray[@]}
      do
        set +f
        for exe in "$target"/$exePattern
        do
          [ -f "$exe" ] || continue
          mkdir -p "$out"/bin
          commandName="$(basename -s .exe "$(echo "$exe" | tr "[A-Z]" "[a-z]")")"
          makeWrapper \
            "${mono}/bin/mono" \
            "$out"/bin/"$commandName" \
            --add-flags "\"$exe\"" \
            ''${makeWrapperArgs}
        done
      done

      runHook postInstall
    '';
  };
in
stdenv.mkDerivation (attrs // (removeAttrs attrsOrig [ "nativeBuildInputs" ]))
